//////////////////////////////////////////////////////////////////////////
// WinDBG extension
// To build environment variable DDK_PATH should be defined
//////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "WindbgExtImpl.h"
#include <specstrings.h>
#include <vector>

#ifndef IMAGE_FILE_MACHINE_ARMNT
#define IMAGE_FILE_MACHINE_ARMNT 0x01c4
#endif

#include <engextcpp.hpp>
#include "../common/ScriptExtInterface.h"
#include "script_loader.h"
#include "perl_loader.h"


LanguageLoader languageLoaders[MaxLanguageId] = {0};


//////////////////////////////////////////////////////////////////////////
// Extension

class EXT_CLASS: public ExtExtension
{
public:
	EXT_CLASS(): debugExt(*this), debugOut(*this)
	{
/*
		if (fopen_s(&m_dbg, "c:\\work\\grayext.log", "a+") != 0)
			m_dbg = NULL;
		log(__FUNCTION__);
*/
	}

	~EXT_CLASS()
	{
/*
		log(__FUNCTION__);

		if (m_dbg)
		{
			fclose(m_dbg);
			m_dbg = NULL;
		}
*/
	}

	LanguageLoader* CheckLoaded(int lang)
	{
		LanguageLoader& loader = languageLoaders[lang];
		if (loader.IsValid())
			return &loader;

		switch (lang)
		{
		case Perl:
			if (LoadPerl(debugOut, loader) != 0)
				return NULL;
			break;
		case Python:
			Err("Python is not supported yet\n");
			return NULL;
			break;
		}

		loader.SetInterface(&debugExt);
		return &loader;
	}

	EXT_COMMAND_METHOD(se);
	EXT_COMMAND_METHOD(perl);
	EXT_COMMAND_METHOD(perl_config);

private:
	WindbgExtImpl debugExt;

	class WindbgOutputImpl: public WindbgOutput
	{
	public:
		WindbgOutputImpl(ExtExtension& _ext):ext(_ext)
		{
		}

		virtual void Out(const char* msg)
		{
			ext.Out(msg);
		}

		virtual void Out(const wchar_t* msg)
		{
			ext.Out(msg);
		}

		virtual int Err(const char* msg, int ret)
		{
			ext.Err(msg);
			return ret;
		}

		virtual int Err(const wchar_t* msg, int ret)
		{
			ext.Err(msg);
			return ret;
		}

	private:
		ExtExtension& ext;
	} debugOut;
};

EXT_DECLARE_GLOBALS();

//////////////////////////////////////////////////////////////////////////
// commands

EXT_COMMAND(se,
			"Execute script",
			"{{custom}}{{l: all arguments are transferred to the script engine}}")
{
}

EXT_COMMAND(perl,
			"Execute Perl script",
			"{{custom}}{{l: all arguments are transferred to the perl}}")
{
	LanguageLoader* loader = CheckLoaded(Perl);
	if (loader == NULL)
		return;

	CallDataHolder call;
	if (debugExt.InitCall(&call) == 0)
	{// ok
		std::string paramsHolder(GetRawArgStr());
		std::vector<const char*> params;
		for (char* prm = const_cast<char*>(paramsHolder.c_str()); prm != NULL; prm = strchr(prm, ' '))
		{
			if (*prm == ' ')
				*(prm++) = 0;
			while (isspace(*prm))
				++prm;
			params.push_back(prm);
		}
		if (params.empty())
		{
			Err("Perl script name is required\n");
		}
		params.push_back(NULL);
		if (loader->RunScript(&params.front()) != 0)
			Err("Error executing Perl script\n");
	}
	else
	{
		Err("Cannot initialize call data\n");
	}
	debugExt.DeinitCall();
}

EXT_COMMAND(perl_config,
			"Configure Perl engine",
			"{find;b;;Search for Perl automatically}{;x,o;path;path to Perl5xx.dll}")
{
	if (HasArg("find"))
	{
		SetPerlPath(debugOut, "");
	}
	else if (HasUnnamedArg(0))
	{
		SetPerlPath(debugOut, GetUnnamedArgStr(0));
	}
	else
	{// no params
		SetPerlPath(debugOut, NULL);
	}
}
